// ------------------------------------------------------------------------
// 版权信息
// 版权归重庆虫儿飞科技有限公司所有。
// 所有权利保留。
// 官方网站：https://netokit.com
// 许可证信息
// Neto.Kit 项目主要遵循 MIT 许可证和 Apache 许可证（版本 2.0）进行分发和使用。
// 许可证的完整文本可以在源代码树根目录中的 LICENSE-APACHE 和 LICENSE-MIT 文件中找到。
// 
// 使用条款
// 使用本代码应遵守相关法律法规和许可证的要求。
// 
// 免责声明
// 对于因使用本代码而产生的任何直接、间接、偶然、特殊或后果性损害，我们不承担任何责任。
// 
// 其他重要信息
// Neto.Kit 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。
// 有关 Neto.Kit 项目的其他详细信息，请参阅位于源代码树根目录中的 COPYRIGHT 和 DISCLAIMER 文件。
// 
// 更多信息
// 请访问 https://netokit.com 获取更多关于 Neto.Kit 项目的许可证和版权信息。
// ------------------------------------------------------------------------

using System.Net;
using System.Net.Sockets;
using System.Numerics;

namespace Neto.Extensions;

/// <summary>
/// 提供与 IP 地址相关的扩展方法。
/// </summary>
public static class IpExtensions
{
    #region IpToLong IP 转换为 long

    /// <summary>
    /// IP 转换为 long
    /// </summary>
    /// <param name="ip"></param>
    /// <returns></returns>
    public static long IpToLong(this string ip)
    {
        char[] separator = ['.'];
        var items = ip.Split(separator);

        return (long.Parse(items[0]) << 24)
               | (long.Parse(items[1]) << 16)
               | (long.Parse(items[2]) << 8)
               | long.Parse(items[3]);
    }

    #endregion IpToLong IP 转换为 long

    #region LongToIP 数字转换为IP

    /// <summary>
    /// 数字转换为IP
    /// </summary>
    /// <param name="ipInt"></param>
    /// <returns></returns>
    public static string LongToIp(this long ipInt)
    {
        var sb = new StringBuilder();
        sb.Append((ipInt >> 24) & 0xFF).Append('.');
        sb.Append((ipInt >> 16) & 0xFF).Append('.');
        sb.Append((ipInt >> 8) & 0xFF).Append('.');
        sb.Append(ipInt & 0xFF);
        return sb.ToString();
    }

    #endregion LongToIP 数字转换为IP

    #region IPV6

    /// <summary>
    /// 将 IPv6 地址字符串转换为等效的 BigInteger 数值表示。
    /// 此方法解析 IPv6 地址并将其字节形式转换为一个大整数（BigInteger）。
    /// </summary>
    /// <param name="ipStr">有效的 IPv6 地址字符串。</param>
    /// <returns>表示 IPv6 地址的 BigInteger 值。</returns>
    /// <remarks>
    /// 该方法将地址字节反转，并添加一个额外的零字节以确保正确的数值顺序。
    /// 示例输入："2400:A480:aaaa:400:a1:b2:c3:d4"
    /// </remarks>
    public static BigInteger IpV6ToLong(this string ipStr)
    {
        // 测试：2400:A480:aaaa:400:a1:b2:c3:d4

        var ip = IPAddress.Parse(ipStr);
        var ipFormat = ip.GetAddressBytes().ToList();
        ipFormat.Reverse();
        ipFormat.Add(0);
        var ipAsInt = new BigInteger(ipFormat.ToArray());
        return ipAsInt;
    }

    /// <summary>
    /// 将IP地址转换为等效的UInt64[2]。
    /// 对于IPv4地址，第一个元素将是0，
    /// 第二个是四个字节的UInt32表示。
    /// 对于IPv6地址，第一个元素将是UInt64
    /// 表示前八个字节，第二个将是
    /// 最后八个字节。
    /// </summary>
    /// <param name="ipAddress">IP地址转换</param>
    /// <returns></returns>
    private static ulong[] IpToUInt64Array(this string ipAddress)
    {
        var addrBytes = IPAddress.Parse(ipAddress).GetAddressBytes();
        if (BitConverter.IsLittleEndian)
        {
            //little-endian machines store multi-byte integers with the
            //least significant byte first. this is a problem, as integer
            //values are sent over the network in big-endian mode. reversing
            //the order of the bytes is a quick way to get the BitConverter
            //methods to convert the byte arrays in big-endian mode.
            //不同的计算机体系结构使用不同的字节顺序存储数据。 “Big-endian”表示最重要的字节位于单词的左端。 “Little-endian”表示最重要的字节位于单词的右端。

            var byteList = new List<byte>(addrBytes);
            byteList.Reverse(); //反转数组
            addrBytes = byteList.ToArray();
        }

        var addrWords = new ulong[2];
        if (addrBytes.Length > 8)
        {
            addrWords[0] = BitConverter.ToUInt64(addrBytes, 8);
            addrWords[1] = BitConverter.ToUInt64(addrBytes, 0);
        }
        else
        {
            addrWords[0] = 0;
            addrWords[1] = BitConverter.ToUInt32(addrBytes, 0);
        }

        return addrWords;
    }

    #endregion IPV6

    /// <summary>
    /// 检查字符串是否为有效的 IP 地址。
    /// </summary>
    /// <param name="ip">要验证的字符串。</param>
    /// <returns>如果字符串是有效 IP 地址，则返回 true；否则返回 false。</returns>
    public static bool IsValidIpAddress(this string ip)
    {
        return IPAddress.TryParse(ip, out _);
    }

    /// <summary>
    /// 验证指定的字符串是否为有效的 IP 地址（IPv4 或 IPv6）。
    /// </summary>
    /// <param name="ip">要验证的字符串。</param>
    /// <returns>如果字符串是有效 IP 地址，则返回 true；否则返回 false。</returns>
    public static string ToFormattedIpAddress(this string ip)
    {
        if (!IsValidIpAddress(ip)) return "Invalid IP";
        var address = IPAddress.Parse(ip);
        return address.AddressFamily == AddressFamily.InterNetworkV6 ? $"[{address}]" : address.ToString();
    }
}